/*
 * Copyright 2013 Agustina Martinez-Garcia

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package uk.ljmu.qudexapps.xmllib.services;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.log4j.Logger;
import org.apache.xmlbeans.XmlError;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.igfay.jfig.JFig;
import org.igfay.jfig.JFigException;

import fedora.fedoraSystemDef.foxml.DatastreamType;
import fedora.fedoraSystemDef.foxml.DatastreamVersionType;
import fedora.fedoraSystemDef.foxml.DigitalObjectDocument;
import fedora.fedoraSystemDef.foxml.XmlContentType;

import uk.da.dext.QudexDocument;
import uk.da.dext.genxml.QudexManagement;
import uk.ljmu.metadata.schemas.SchemaElem;
import uk.ljmu.metadata.schemas.SchemaInstance;
import uk.ljmu.metadata.schemasutilities.DcUtilities;
import uk.ljmu.metadata.schemasutilities.SchemasProcessing;
import uk.ljmu.metadata.utilities.MetadataUtilities;
import uk.ljmu.metadata.utilities.MetadataUtilities.ValueType;
import uk.ljmu.mulgaraifz.errorhandling.MulgaraException;
import uk.ljmu.qudexapps.xmllib.exceptions.FoxmlException;
import uk.ljmu.qudexapps.xmllib.model.ColumnMap;
import uk.ljmu.qudexapps.xmllib.model.FoxmlBeans;
import uk.ljmu.qudexapps.xmllib.utilities.Constants;
import uk.ljmu.qudexapps.xmllib.utilities.SpreadsheetUtilities;
import uk.ljmu.qudexapps.xmllib.utilities.SpreadsheetUtilities.Column;
import uk.ljmu.qudexapps.xmllib.validators.QudexFoxmlValidator;
import uk.ljmu.qudexapps.xmllib.validators.QudexFoxmlValidatorImpl;

/**
 * Management of all the repository update operations over collections and
 * documents
 * 
 * @author agustina
 * 
 */
public class UpdateOperationsImpl implements UpdateOperations {

	/**
	 * ROOT LOGGER
	 */
	private static Logger log = Logger.getLogger(UpdateOperationsImpl.class);

	/**
	 * Spreadsheet Management instance
	 */
	private SpreadsheetManagement spService;
	/**
	 * Management of the progress bar indicating execution percentages
	 */
	private String progressStatus;

	/**
	 * Manager for the Qudex XML instance generation
	 */
	private QudexManagement qudexManager;

	/**
	 * Object that manages Fedora related operations, e.g. collection creation,
	 * add datastreams
	 */
	private QudexFedoraObjects quObject;

	private String fileType;

	private boolean isQudexCollections;

	private FoxmlBeans foxmlBeans;

	private RelationshipsManagement relsManagement;

	public String getProgressStatus() {
		return progressStatus;
	}

	public void setProgressStatus(String progressStatus) {
		this.progressStatus = progressStatus;
	}

	/**
	 * Constructor
	 */
	public UpdateOperationsImpl(String typeSpreadsheet, boolean isCollections)
			throws FoxmlException, JFigException {

		this.fileType = typeSpreadsheet;
		this.quObject = new QudexFedoraObjects();
		this.quObject.setFedoraUser(JFig.getInstance().getValue("fedoraServer",
				"user"));
		this.foxmlBeans = new FoxmlBeans(this.quObject.getFedoraObject());
		this.progressStatus = "0%";
		this.isQudexCollections = isCollections;
		this.qudexManager = new QudexManagement();
		this.relsManagement = new RelationshipsManagement();
	}

	/*
	 * UPDATE METHODS
	 */
	/**
	 * @param spreadsheet
	 * @throws FoxmlException
	 * 
	 */
	public void updateCollections(InputStream spreadsheet)
			throws FoxmlException {
		float increment = 0;
		boolean resultsOK = false;
		int leftNumberPasses = 0, counter = 1;

		// Clean array of results from previous iterations
		this.quObject.getObjResults().clear();
		this.progressStatus = "0%";
		// Init FedoraObject in FoxmlBeans
		this.foxmlBeans.initFedoraObject(this.quObject.getFedoraObject());
		resultsOK = getSpreadsheetManager();
		if (!resultsOK)
			throw new FoxmlException("File is neither CSV or EXCEL...");
		try {
			// READ SPREADSHEET
			resultsOK = spService.readSpreadsheet(spreadsheet);
			// Initialise the progress bar
			this.progressStatus = "15%";
			if (!resultsOK) {
				log.fatal("Errors while parsing spreadsheet...");
				this.quObject.addFedoraResultsMessage(
						"Error in updateCollections", "");
				return;
			}
		} catch (Exception ex) {
			log.fatal("Exceptions while parsing spreadsheet: "
					+ ex.getMessage());
			this.quObject.addFedoraResultsMessage(
					"Error in updateCollections, check log for more details.",
					"");
			return;
		}
		FedoraMulgaraService fedMulgaraObject = null;
		try {
			fedMulgaraObject = new FedoraMulgaraServiceImpl();
		} catch (JFigException e) {
			log.error(
					"Error while reading configuration file, FedoraMulgaraService",
					e);
			this.quObject
					.addFedoraResultsMessage(
							"Error while connecting to Mulgara, check log for more details.",
							"");
			return;
		}
		// AMG- Progress Bar
		leftNumberPasses = this.spService.getSpreadsheetMap().getGlobalMap()
				.size();
		increment = (80 / (float) leftNumberPasses);

		// AMG- EXTERNAL LOOP FOR EACH ROW IN THE MAP STRUCTURE
		for (ColumnMap item : spService.getSpreadsheetMap().getGlobalMap()
				.values()) {
			// this.foxmlBeans.initFoxmlDocumentObject();
			// OBTAIN QUDEX INSTANCE FOR THE CURRENT FEDORA OBJECT
			resultsOK = getCollectionQudexInstance(item, fedMulgaraObject);
			if (!resultsOK) {
				log.fatal("Error while reading configuration file, FedoraMulgaraService");
				this.quObject
						.addFedoraResultsMessage(
								"manager",
								"Errors while obtaining Qudex Instance from the Root Collection, revise the spreadsheet objects.");
				return;
			}
			// Check the type of operation: UPDATE OR DELETE
			// CHECK if update/delete operations in Object
			String existingObjectPid = "";

			// Find PID in spreadsheet, if not present ERROR
			existingObjectPid = findCollectionPid(item.getIdentifier());
			if (existingObjectPid == null) {
				log.error("Trying to delete/update a collection without having specified its PID in the spreadsheet.");
				this.quObject
						.addFedoraResultsMessage(
								"manager",
								"Trying to delete a collection without having specified its PID in the spreadsheet.");
				continue;
			}
			if (!isDeleteOperation(item)) {
				// Check whether is update operation
				if (!isUpdateOperation(item)) {
					// This Object remains as it is, no operations on it
					this.quObject
							.addFedoraResultsMessage(
									"manager",
									"Object with PID "
											+ existingObjectPid
											+ " has not been modified. NO OPERARION SPECIFIED");
					int auxInt = Math.round((float) increment * counter + 20);
					this.progressStatus = Long.toString(auxInt) + "%";
					counter++;
					continue;
				} else {
					// Specific method for UPDATING A COLLECTION
					resultsOK = updateQudexCollection(item, fedMulgaraObject,
							existingObjectPid);
					if (!resultsOK) {
						// LOG HERE
						this.quObject
								.addFedoraResultsMessage(
										"manager",
										"Object with PID "
												+ existingObjectPid
												+ " has not been modified. ERRORS UPDATING");
						int auxInt = Math.round((float) increment * counter
								+ 20);
						this.progressStatus = Long.toString(auxInt) + "%";
						counter++;
						continue;
					}
				}
			} else {
				// Specific method for DELETING A COLLECTION
				resultsOK = deleteQudexCollection(item, fedMulgaraObject,
						existingObjectPid);
				if (!resultsOK) {
					this.quObject
							.addFedoraResultsMessage(
									"manager",
									"Object with PID "
											+ existingObjectPid
											+ " has not been modified. ERRORS DELETING");
					int auxInt = Math.round((float) increment * counter + 20);
					this.progressStatus = Long.toString(auxInt) + "%";
					counter++;
					continue;
				}
			}
			// AMG- Progress bar update
			// 20% estimated for spreadsheet parsing initially
			int auxInt = Math.round((float) increment * counter + 20);
			this.progressStatus = Long.toString(auxInt) + "%";
			counter++;
		}
		if (!this.progressStatus.equals("100%"))
			this.progressStatus = "100%";
		// AMG- Give time to AJAX calls to update when using progress bars
		// in the user interface
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			log.error("Error in updateCollections", e);
		}
	}

	/**
	 * 
	 * @param item
	 * @param fedMulgaraObject
	 * @param existingObjectPid
	 * @return boolean
	 */
	private boolean deleteQudexCollection(ColumnMap item,
			FedoraMulgaraService fedMulgaraObject, String existingObjectPid) {
		boolean resultsOK;

		// Manage different cases: deleting a ROOT COLLECTION, DELETING A
		// SUB-COLLECTION
		try {
			if (!fedMulgaraObject.isSubCollection(existingObjectPid)) {
				// Delete a root collection
				resultsOK = this.quObject.deleteQudexCollectionRoot(
						existingObjectPid, true, this.relsManagement);
				if (resultsOK)
					this.quObject
							.addFedoraResultsMessage("manager",
									"Object with PID " + existingObjectPid
											+ " deleted");
			} else {
				// Deleting a sub-collection

				// Is a leaf sub-collection?
			}

			return false;
		} catch (MulgaraException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		}
	}

	/**
	 * 
	 * @param item
	 * @param fedMulgaraObject
	 * @param existingObjectPid
	 * @return
	 */
	private boolean updateQudexCollection(ColumnMap item,
			FedoraMulgaraService fedMulgaraObject, String existingObjectPid) {
		StringBuilder sb = new StringBuilder();
		boolean resultsOK = true;

		// Load FOXML Object
		String foxmlXml = this.quObject.getFedoraObject().getObjectXML(
				existingObjectPid);
		if (foxmlXml == null) {
			log.fatal("Error while retrieving object's Foxml contents...");
			return false;
		}
		DigitalObjectDocument doc;
		try {
			doc = DigitalObjectDocument.Factory.parse(foxmlXml);
			this.foxmlBeans.setFoxmlDoc(doc);
		} catch (XmlException e1) {
			log.error("In updateQudexCollection: ", e1);
			return false;
		}

		// MODIFY RELS-EXT DS and DC DS
		resultsOK = false;
		resultsOK = modifyDublinCoreDS(item);
		if (!resultsOK) {
			this.quObject.addFedoraResultsMessage("manager",
					"Error while updating DC record of the object with PID: "
							+ this.foxmlBeans.getFoxmlDoc().getDigitalObject()
							+ "'.");
			return false;
		}
		resultsOK = modifyRelsExtDS(item);
		if (!resultsOK) {
			this.quObject.addFedoraResultsMessage("manager",
					"Error while updating RELS-EXT record of the object with PID: "
							+ this.foxmlBeans.getFoxmlDoc().getDigitalObject()
							+ "'.");
			return false;
		}

		// FOXML Validation. Create an XmlOptions instance and
		// set the error listener.
		XmlOptions validateOptions = new XmlOptions();
		ArrayList<XmlError> errorList = new ArrayList<XmlError>();
		validateOptions.setErrorListener(errorList);
		resultsOK = this.foxmlBeans.getFoxmlDoc().validate(validateOptions);
		if (!resultsOK) {
			for (int i = 0; i < errorList.size(); i++) {
				XmlError error = (XmlError) errorList.get(i);
				sb.append("RESULTS OF VALIDATION \nMessage: ");
				sb.append(error.getMessage());
				sb.append("\n");
				sb.append(error.getCursorLocation().xmlText());
				log.fatal(sb.toString());
				sb.delete(0, sb.toString().length());
			}
			log.fatal("Error while validating FOXML instance");
			this.quObject.addFedoraResultsMessage("manager",
					"Error while validating Document FOXML instance for object with pid "
							+ this.foxmlBeans.getFoxmlDoc().getDigitalObject()
							+ " title '" + this.foxmlBeans.getFoxmlLabel()
							+ "'.");
			return false;
		}

		// AMG- (6) CALL TO INGEST METHOD
		sb.append(Constants.XML_VERSION_HEADER);
		sb.append(this.foxmlBeans.getFoxmlDoc().toString());
		String results = "";
		sb.delete(0, sb.toString().length());
		//results = this.quObject.updateObjectFedora(this.foxmlBeans);
		results = this.quObject.updateFullObject(this.foxmlBeans);
		if (results == null) {
			results = "Error updating object. Contact Administrator";
		}
		if (!results.startsWith(Constants.DEF_QUDEX_NS)) {
			// RESULTS OBJECT CREATION AND PARSE OF INGESTION RESULTS
			this.quObject
					.addFedoraResultsMessage(this.foxmlBeans.getFoxmlDoc()
							.getDigitalObject().getPID(),
							"Errors while storing document in Fedora. Contact administrator.");
		} else {
			this.quObject.addFedoraResultsMessage("manager",
					"Updated collection with Pid: " + results);
		}

		resultsOK = this.quObject.addSpreadsheetToObject("COLLECTION_SP",
				this.foxmlBeans.getFoxmlDoc().getDigitalObject().getPID(),
				this.fileType);
		if (!resultsOK) {
			this.quObject
					.addFedoraResultsMessage(this.foxmlBeans.getFoxmlDoc()
							.getDigitalObject().getPID(),
							"Errors while adding Ingestion Spreadsheet to collection object.");
			return false;
		}

		return true;
	}

	private boolean modifyRelsExtDS(ColumnMap item) {
		List<String> namespacesList = new ArrayList<String>();
		QudexFoxmlValidator qudexValidator = null;
		Iterator<SchemaInstance> it = this.spService
				.getSchemaInstanceContainer().getListSchemaInstances()
				.iterator();
		boolean valid = true, resultsOK;
		StringBuilder sb = new StringBuilder();
		String namespacePrefix = "";

		// AMG- Create empty document
		Document doc = getRelsExtDocument();
		if (doc == null) {
			doc = SchemasProcessing.contructRDFDocument(this.foxmlBeans
					.getFoxmlDoc().getDigitalObject().getPID(),
					new ArrayList<String>());
		}
		try {
			// AMG- Create Qudex validator
			qudexValidator = new QudexFoxmlValidatorImpl(
					this.isQudexCollections);

			while (it.hasNext()) {
				SchemaInstance elem = (SchemaInstance) it.next();
				// Very important preserve namespaces
				ValueType columnPrefix = MetadataUtilities.MDSchemas.get(elem
						.getSchemaPrefix());
				if (MetadataUtilities.MDSchemas.get(elem.getSchemaPrefix()
						.toLowerCase()) != MetadataUtilities.ValueType.DcElement) {
					List<String> list = new ArrayList<String>();
					list.add(elem.getSchemaPrefix().toLowerCase());
					SchemasProcessing.addNamespacesToDocument(doc, list);
					if (doc == null) {
						return false;
					}
					namespacePrefix = elem.getSchemaPrefix().toLowerCase();
					namespacesList.add(elem.getSchemaPrefix().toLowerCase());
				}

				for (SchemaElem schemaElem : elem.getSchemaElemsList()) {
					String property, value = "";
					for (Column col : ((ColumnMap) item).getColumnMap()
							.keySet()) {
						if (columnPrefix.equals(col.getColumnType())
								&& col.getColumnName().equalsIgnoreCase(
										schemaElem.getElementName())) {
							// Qualified property
							sb.append(elem.getSchemaPrefix());
							sb.append(Constants.PID_DELIMITER);
							sb.append(col.getColumnName());
							property = sb.toString();
							sb.delete(0, sb.toString().length());
							String results = "";
							try {
								results = spService
										.returnStringCellContent(((ColumnMap) item)
												.getColumnMap().get(col));
								value = results;
							} catch (Exception ex) {
								if (ex.getMessage() != "")
									log.fatal(ex.getMessage());
								else
									log.error("Error: ", ex);

								return false;
							}
							// AMG - Normal RDF
							if (value.contains(Constants.FIELDS_SEPARATOR)) {
								String[] arrayValues = value
										.split(Constants.FIELDS_SEPARATOR);
								for (String str : arrayValues) {
									// AMG Check if property is collection
									// membership
									if (RelationshipsManagement
											.isRelationshipProperty(property)) {
										// For now we ignore relationships
										// properties
										continue;
									} else {
										resultsOK = SchemasProcessing
												.addRDFProperty(
														namespacePrefix,
														property,
														str,
														schemaElem
																.getPropertyValueType(),
														doc);
										if (!resultsOK) {
											continue;
										}
									}
								}
							} else {
								// The property has to come qualified, e.g.
								// rel:isMemberOf
								if (RelationshipsManagement
										.isRelationshipProperty(property)) {
									// We ignore relationships properties
									continue;
								} else {
									resultsOK = SchemasProcessing
											.addRDFProperty(
													namespacePrefix,
													property,
													value,
													schemaElem
															.getPropertyValueType(),
													doc);
									if (!resultsOK) {
										continue;
									}
								}
							}
						}
					}
				}
			}
			sb.append("RELS-EXT record for the object");
			// AMG Check if rdf:type collection is present
			if (this.isQudexCollections) {
				Element desc = doc.getRootElement().element("Description");
				if (desc == null) {
					desc = doc.getRootElement().element("rdf:Description");
					if (desc == null)
						return false;
				}
				boolean typePresent = SchemasProcessing.checkPropertyExistence(
						desc, "type",
						MetadataUtilities.DCMI_ELEMENTS.get("collection"))
						|| SchemasProcessing.checkPropertyExistence(desc,
								"rdf:type", MetadataUtilities.DCMI_ELEMENTS
										.get("collection"));
				if (!typePresent) {
					resultsOK = SchemasProcessing
							.addRDFProperty("rdf", "rdf:type",
									MetadataUtilities.DCMI_ELEMENTS
											.get("collection"),
									MetadataUtilities.propertyTypes.Resource
											.toString(), doc);
					if (!resultsOK) {
						this.quObject
								.addFedoraResultsMessage("manager",
										"Errors in Spreadsheet. Collection Type missing. Check Spreadsheet");
						return false;
					}
				}
			}
			valid = qudexValidator.validateQudexElements(doc.asXML(),
					namespacesList, this.quObject);
			if (valid) {
				// AMG- For Qudex, non DC elements go into RELS-EXT DS,
				// with subject the OBJECT PID
				/*
				 * this.foxmlBeans.addDatastream(
				 * Constants.DEFAULT_RELSEXT_DS_NAME,
				 * DatastreamType.CONTROLGROUP.X, sb.toString(), StateType.A,
				 * doc.asXML(), Constants.DEFAULT_MIMETYPE, false);
				 */
				int idx = 0;
				for (DatastreamType dsType : this.foxmlBeans.getFoxmlDoc()
						.getDigitalObject().getDatastreamArray()) {
					if (dsType.getID()
							.equals(Constants.DEFAULT_RELSEXT_DS_NAME)) {
						DatastreamType auxDsType = this.foxmlBeans
								.getFoxmlDoc().getDigitalObject()
								.getDatastreamArray()[idx];
						String id = auxDsType.getDatastreamVersionArray()[0].getID();
						auxDsType.removeDatastreamVersion(0);
						DatastreamVersionType dsVersion = auxDsType
								.addNewDatastreamVersion();
						dsVersion.setID(id);
						dsVersion.setMIMETYPE(MetadataUtilities.MIMETYPES_MAP
								.get("rdf"));
						XmlContentType content = dsVersion.addNewXmlContent();
						content.set(XmlContentType.Factory.parse(doc.asXML()));
						break;
					}
					idx++;
				}
				sb.delete(0, sb.toString().length());
			} else {
				this.quObject
						.addFedoraResultsMessage("manager",
								"Check spreadsheet headers. Validation against Spreadsheet Schema failed.");
				return false;
			}

			return true;
		} catch (Exception ex) {
			log.fatal(ex.getMessage());
			return false;
		} finally {
			qudexValidator.cleanValidator();
		}

	}

	private Document getRelsExtDocument() {
		Document doc = DocumentHelper.createDocument();
		boolean found = false;

		for (DatastreamType dsType : this.foxmlBeans.getFoxmlDoc()
				.getDigitalObject().getDatastreamArray()) {
			if (dsType.getID().equals(Constants.DEFAULT_RELSEXT_DS_NAME)) {
				if (dsType.getVERSIONABLE()) {
					DatastreamVersionType dsVersion = dsType
							.getDatastreamVersionArray()[dsType
							.getDatastreamVersionArray().length - 1];
					try {
						doc = DocumentHelper.parseText(dsVersion
								.getXmlContent().toString());
						// Remove all properties but relationship ones
						doc = this.removeRdfProperties(doc);
						found = true;
					} catch (DocumentException e) {
						log.error("getRelsExtDocument: ", e);
						return null;
					}
				}
			}
		}
		if (found)
			return doc;
		else
			return null;
	}

	private Document removeRdfProperties(Document doc) {
		Element root = doc.getRootElement();
		Element description = root.element("Description");
		if (description != null) {
			Iterator<?> it = description.elementIterator();
			while (it.hasNext()) {
				Element elem = (Element) it.next();
				if (elem.getNamespace().equals(
						MetadataUtilities.SCHEMAS_PREFIXES.rel)) {
					continue;
				} else {
					description.elements().remove(elem);
				}
			}
			return doc;
		}
		return null;
	}

	/**
	 * @param spreadsheet
	 */
	public void updateFiles(InputStream spreadsheet) {
		// READ SPREADSHEET

	}

	/*
	 * AUXILIAR FUNCTIONS
	 */
	/**
	 * 
	 * @return
	 */
	private boolean getSpreadsheetManager() {
		// AMG- CHECK fileType
		this.spService = SpreadsheetUtilities.getSpreadsheetManager(
				this.fileType, true, this.isQudexCollections);
		if (this.spService == null) {
			log.fatal("File is neither a CSV file or Excel file...");
			return false;
		}
		return true;
	}

	/**
	 * Associates the current QudexManager with the Qudex Instance from the
	 * parent collection of the current object which is going to be updated
	 * 
	 * @param item
	 *            current working object from the updates spreadsheet
	 * @param fedMulgaraObject
	 *            instance of the FedoraMulgara operations object
	 */
	private boolean getCollectionQudexInstance(ColumnMap item,
			FedoraMulgaraService fedMulgaraObject) {
		boolean resultsOK;
		try {
			String currentObjectPID = findCollectionPid(item.getIdentifier());
			if (currentObjectPID == null) {
				log.fatal("Errors while finding object identifier in spreadsheet, revise the Update Source Spreadsheet");
				this.quObject
						.addFedoraResultsMessage(
								"Errors while finding object identifier in spreadsheet, revise the Update Source Spreadsheet.",
								"");
				return false;
			}
			fedMulgaraObject.findRootCollection(currentObjectPID);
			String currentRootCollection = fedMulgaraObject
					.getRootCollectionPid();

			resultsOK = this.quObject
					.isCollectionWithQudexInstance(currentRootCollection);
			if (resultsOK) {
				String instance = this.quObject
						.getCollectionWithQudexInstance(currentRootCollection);
				if (instance != null) {
					try {
						this.qudexManager
								.setQudexInstance(QudexDocument.Factory
										.parse(instance));
					} catch (XmlException e) {
						log.error("Error in manager:", e);
						this.quObject
								.addFedoraResultsMessage("manager",
										"Error retrieving Qudex Instance from selected Collection");
						return false;
					}
				} else {
					this.quObject
							.addFedoraResultsMessage("manager",
									"Error retrieving Qudex Instance from selected Collection");
					return false;
				}
			} else {
				return false;
			}
		} catch (MulgaraException e) {
			log.error("Errors in findRootCollection: ", e);
			this.quObject
					.addFedoraResultsMessage(
							"Error while connecting to Mulgara, check log for more details.",
							"");
			return false;
		}
		return true;
	}

	private boolean isUpdateOperation(ColumnMap item) {
		boolean isUpdate = false;

		for (Column col : item.getColumnMap().keySet()) {
			if (col.getColumnName()
					.equalsIgnoreCase(Constants.QUDEX_UPDATE_PID)) {
				isUpdate = Boolean.valueOf(this.spService
						.returnStringCellContent(item.getColumnMap().get(col)));

				return isUpdate;
			}
		}
		return false;
	}

	private boolean isDeleteOperation(ColumnMap item) {
		boolean isDelete = false;
		for (Column col : item.getColumnMap().keySet()) {
			if (col.getColumnName()
					.equalsIgnoreCase(Constants.QUDEX_DELETE_PID)) {
				isDelete = Boolean.valueOf(this.spService
						.returnStringCellContent(item.getColumnMap().get(col)));

				return isDelete;
			}
		}
		return false;
	}

	private String findCollectionPid(String rowIdentifier) {
		return (String) this.spService.getRowColumnValue(rowIdentifier,
				Constants.PID_LABEL);
	}

	/**
	 * 
	 * @param item
	 *            actual item being inspected from the row contents of the
	 *            spreadsheet
	 * @return true if DC ds has been modified successfully, false otherwise
	 */
	private boolean modifyDublinCoreDS(ColumnMap item) {
		QudexFoxmlValidator qudexValidator = null;
		Iterator<SchemaInstance> it = this.spService
				.getSchemaInstanceContainer().getListSchemaInstances()
				.iterator();
		boolean valid = true, resultsOK;
		StringBuilder sb = new StringBuilder();

		// AMG- Create empty document
		Document docDC = DocumentHelper.createDocument();
		try {
			// AMG- Create Qudex validator
			qudexValidator = new QudexFoxmlValidatorImpl(
					this.isQudexCollections);
			while (it.hasNext()) {
				SchemaInstance elem = (SchemaInstance) it.next();
				// Very important preserve namespaces
				ValueType columnPrefix = MetadataUtilities.MDSchemas.get(elem
						.getSchemaPrefix());
				// Parse only DC elements
				if (MetadataUtilities.MDSchemas.get(elem.getSchemaPrefix()
						.toLowerCase()) == MetadataUtilities.ValueType.DcElement) {
					docDC = DcUtilities.constructDCEmptyDocument();
				} else
					continue;

				for (SchemaElem schemaElem : elem.getSchemaElemsList()) {
					String property, value = "";
					for (Column col : ((ColumnMap) item).getColumnMap()
							.keySet()) {
						if (columnPrefix.equals(col.getColumnType())
								&& col.getColumnName().equalsIgnoreCase(
										schemaElem.getElementName())) {
							// Qualified property
							sb.append(elem.getSchemaPrefix());
							sb.append(Constants.PID_DELIMITER);
							sb.append(col.getColumnName());
							property = sb.toString();
							sb.delete(0, sb.toString().length());
							String results = "";
							try {
								results = spService
										.returnStringCellContent(((ColumnMap) item)
												.getColumnMap().get(col));
								value = results;
							} catch (Exception ex) {
								if (ex.getMessage() != "")
									log.fatal(ex.getMessage());
								else
									log.error("Error: ", ex);

								return false;
							}
							if (value.contains(Constants.FIELDS_SEPARATOR)) {
								String[] arrayValues = value
										.split(Constants.FIELDS_SEPARATOR);
								for (String str : arrayValues) {
									resultsOK = DcUtilities.addOAIDCproperty(
											property, str, docDC);
									if (!resultsOK) {
										log.error("DC property not added: "
												+ property);
									}
								}
							} else {
								resultsOK = DcUtilities.addOAIDCproperty(
										property, value, docDC);
								if (!resultsOK) {
									log.error("DC property not added: "
											+ property);
								}
							}
						}
					}
				}
				List<String> dcNs = new ArrayList<String>();
				dcNs.add("dc");

				valid = qudexValidator.validateQudexElements(docDC.asXML(),
						dcNs, this.quObject);
				if (valid) {
					int idx = 0;
					// AMG replace instance Dublin Core datastream
					for (DatastreamType dsType : this.foxmlBeans.getFoxmlDoc()
							.getDigitalObject().getDatastreamArray()) {
						if (dsType.getID().equals(FoxmlBeans.DC_DS_NAME)) {
							if (dsType.getVERSIONABLE()) {
								DatastreamType auxDsType = this.foxmlBeans
										.getFoxmlDoc().getDigitalObject()
										.getDatastreamArray()[idx];
								DatastreamVersionType dsVersion = auxDsType
										.addNewDatastreamVersion();

								String dsId = FoxmlBeans
										.getNextVersionDS(auxDsType
												.getDatastreamVersionArray()[dsType
												.getDatastreamVersionArray().length - 2]
												.getID());
								if (dsId == null)
									return false;
								dsVersion.setID(dsId);
								dsVersion
										.setMIMETYPE(MetadataUtilities.MIMETYPES_MAP
												.get("xml"));
								XmlContentType content = dsVersion
										.addNewXmlContent();
								try {
									content.set(XmlObject.Factory.parse(docDC
											.asXML()));
								} catch (XmlException e) {
									log.fatal(e.getMessage());
									return false;
								}
							} else {
								if (dsType.getDatastreamVersionArray().length != 1) {
									log.error("In modifyDublinCoreDS: non versionable DS with multiple version");
									return false;
								} else {
									DatastreamType auxDsType = this.foxmlBeans
											.getFoxmlDoc().getDigitalObject()
											.getDatastreamArray()[idx];
									auxDsType.removeDatastreamVersion(0);
									DatastreamVersionType dsVersion = auxDsType
											.addNewDatastreamVersion();
									XmlContentType content = dsVersion
											.addNewXmlContent();
									try {
										content.set(XmlObject.Factory
												.parse(docDC.asXML()));
									} catch (XmlException e) {
										log.fatal(e.getMessage());
										return false;
									}
								}
							}
							break;
						}
						idx++;
					}
				} else {
					this.quObject
							.addFedoraResultsMessage(
									"manager",
									"Check spreadsheet Dublin Core headers. Validation against Spreadsheet Schema failed.");
					return false;
				}
			}

			return true;
		} catch (Exception ex) {
			log.error("Error in modifyDublinCoreDS: ", ex);
			return false;
		} finally {
			qudexValidator.cleanValidator();
		}
	}
}
